<template>
  <div class="table-panel" ref="bcTableRef">
    <el-table
      ref="tableRef"
      v-loading="loading"
      :data="tableData"
      :height="elTableHright"
      class="bc-table"
      v-bind="props.attrs"
      :row-key="props.rowKey"
      @selection-change="handleSelectionChange"
      @cell-click="cellClick"
    >
      <el-table-column v-if="props.selection" fixed="left" width="55" type="selection" :reserve-selection="true" />
      <el-table-column v-if="props.radio" fixed="left" width="55">
        <template #default="{ row }">
          <el-radio
            v-model="radioValue[props.rowKey]"
            :value="row[props.rowKey]"
            label=""
            @input="handleRadioChange(row, props.rowKey)"
          />
        </template>
      </el-table-column>
      <template v-for="(item, index) in columns" :key="index">
        <el-table-column
          :fixed="item.fixed"
          :label="item.label"
          :prop="item.textProp || item.prop"
          :min-width="item.width"
          :show-overflow-tooltip="item.tooltip === false ? false : true"
        >
          <template #default="scope">
            <slot :name="item.slotName ? item.slotName : ''" :scope="{ row: scope.row, $index: scope.$index }">
              <!-- 序号 -->
              <span v-if="item.type === 'index'">{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
              <!-- 选择 -->
              <span v-else-if="item.type === 'select'" v-html="selectComputed(item, scope.row[item.prop])"></span>
              <!-- 单选 -->
              <span v-else-if="item.type === 'radio'" v-html="selectComputed(item, scope.row[item.prop])"></span>
              <!-- 图片 -->
              <div class="img-box" v-else-if="item.type === 'image'" v-viewer>
                <img v-for="(item, index) in scope.row[item.prop]" :key="index" :src="item.url" alt="" />
              </div>
              <!-- 时间 -->
              <span v-else-if="['date', 'month', 'datetime'].includes(item.type)">
                {{ dayComputed(item, scope.row[item.prop]) }}
              </span>
              <!-- 文本/其他 -->
              <span v-else>{{ scope.row[item.prop] }}</span>
            </slot>
          </template>
        </el-table-column>
      </template>
    </el-table>
    <div class="bc-page" v-if="isPage">
      <el-pagination
        v-model:current-page="pageNum"
        v-model:page-size="pageSize"
        :page-sizes="[10, 20, 30, 50, 100]"
        :layout="props.paginationLayout"
        :total="pageTotal"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>

<script setup>
import dayjs from 'dayjs'
import { reactive, watch } from 'vue'

const props = defineProps({
  /**
   * columns配置项
   * type: select|date|month|datetime|number
   * prop: table key
   * label: 字段名
   * options: select专用，枚举项[{value:"",label:""}]
   * slotName: 自定义列插槽名称
   * width: 列宽度
   * fixed: 固定左右侧
   * format: date|month|datetime 专用，时间展示格式
   */
  columns: {
    type: Array,
    default: () => [],
  },
  // 通过接口获取数据
  dataSource: {
    type: [Function, Array],
    default: () => [],
  },
  // 查询条件
  query: {
    type: Object,
    default: () => {},
  },
  // 页面留白高度
  pageSpaceHeight: {
    type: Number,
    default: 16,
  },
  // 是否分页
  isPage: {
    type: Boolean,
    default: true,
  },
  // 表格默认高度（不包含分页器），单位px
  defaultHeight: {
    type: [String, Number],
  },
  // 多选
  selection: {
    type: Boolean,
    default: false,
  },
  // 单选
  radio: {
    type: Boolean,
    default: false,
  },
  // 多选｜单选绑定字段名
  rowKey: {
    type: String,
    default: 'id',
  },
  // element table 的属性
  attrs: {
    type: Object,
    default: () => {
      return {
        stripe: true,
      }
    },
  },
  events: {},
  otherQuery: {},
  paginationLayout: {
    type: String,
    default: 'total, sizes, prev, pager, next, jumper',
  },
})
const emits = defineEmits(['selection-change', 'radio-change', 'cell-click'])
watch(
  () => ({ ...props.otherQuery }),
  () => {
    console.log('出发1')

    load()
  },
  { deep: true }
)

// 分页器
const pageNum = ref(1)
const pageSize = ref(20)
const pageTotal = ref(0)
// 页数切换
const handleSizeChange = (val) => {
  load()
}
// 页码切换
const handleCurrentChange = (val) => {
  load()
}

// 表格数据
const loading = ref(false)
const tableData = ref([])
const load = async () => {
  loading.value = true
  const params = {
    ...props.query,
  }
  if (props.isPage) {
    params['pageNum'] = pageNum.value
    params['pageSize'] = pageSize.value
  }
  if (Array.isArray(props.dataSource)) {
    // 判断当前传入是否为数组
    if (props.isPage) {
      tableData.value = props.dataSource.slice((pageNum.value - 1) * pageSize.value, pageNum.value * pageSize.value)
      pageTotal.value = props.dataSource.length
    } else {
      tableData.value = props.dataSource
    }
  } else if (typeof props.dataSource === 'function') {
    // 判断数据源是否为异步函数
    if (props.dataSource.constructor.name === 'AsyncFunction' || props.dataSource().then) {
      try {
        const res = await props.dataSource(params, props.otherQuery)
        tableData.value = res?.rows || []
        pageTotal.value = res?.total || 0
      } catch (e) {
        tableData.value = []
        pageTotal.value = 0
        ElMessage.error(e.message)
      } finally {
        loading.value = false
      }
    } else {
      const res = props.dataSource(params)
      tableData.value = res?.rows || []
      pageTotal.value = res?.total || 0
    }
  } else {
    tableData.value = []
    pageTotal.value = 0
  }
  loading.value = false
}
watch(
  () => props.query,
  (newVal, oldVal) => {
    load()
  },
  { deep: true, immediate: true }
)
// 时间处理
const dayComputed = (option, value) => {
  if (value) {
    const typeObj = {
      datetime: { label: '时间', format: 'YYYY-MM-DD HH:mm:ss' },
      date: { label: '日期', format: 'YYYY-MM-DD' },
      month: { label: '月份', format: 'YYYY-MM' },
    }
    return dayjs(value).format(option?.format || typeObj[option.type].format)
  } else {
    return ''
  }
}
// 选择器处理
const enums = reactive({})
function hexToRgba(hexColor, alpha = 1) {
  // 移除#号（如果有的话）
  hexColor = hexColor.replace(/^#/, '')

  // 处理3位简写形式
  if (hexColor.length === 3) {
    hexColor = hexColor
      .split('')
      .map((char) => char + char)
      .join('')
  }

  // 提取红、绿、蓝值
  const r = parseInt(hexColor.substr(0, 2), 16)
  const g = parseInt(hexColor.substr(2, 2), 16)
  const b = parseInt(hexColor.substr(4, 2), 16)

  // 返回RGBA格式的字符串
  return `rgba(${r}, ${g}, ${b}, ${alpha})`
}
const selectComputed = (config, value) => {
  if (enums.hasOwnProperty(config.prop)) {
    const arr = enums[config.prop]
    if (arr && Array.isArray(value)) {
      return value
        .map((i) => {
          const index = (arr || []).findIndex((j) => j.value == i)
          if (index >= 0) {
            return arr[index].label
          } else {
            return value || ''
          }
        })
        .join(',')
    } else if (arr && (value || value === 0)) {
      const optionIndex = arr.findIndex((i) => i.value == value)
      if (optionIndex >= 0) {
        return `
      ${arr[optionIndex].color ? `<span style='padding: 0 13px;height:20px;line-height:20px;border-radius:10px;background:${hexToRgba(arr[optionIndex].color, 0.1)}; color:${arr[optionIndex].color}'><span>${arr[optionIndex].label}</span></span>` : `<span>${arr[optionIndex].label}</span>`}
        `
      } else {
        return value || ''
      }
    } else {
      return value || ''
    }
  } else {
    getValueEnums(config)
  }
}
const getValueEnums = async (config) => {
  // 返回数组
  if (Array.isArray(config.options)) {
    initOptions(config.options, config.prop)
    // 返回方法
  } else if (typeof config.options === 'function') {
    const result = config.options()
    if (result.then) {
      initOptions([], config.prop)
      result
        .then((list) => {
          initOptions(list, config.prop)
        })
        .catch((err) => {
          initOptions([], config.prop)
        })
    } else if (Array.isArray(result)) {
      initOptions(result, config.prop)
    } else {
      initOptions([], config.prop)
    }
  }
}
// 赋值
const initOptions = (options, value) => {
  enums[value] = options || []
}

// 多选
const selectionData = ref([])
const handleSelectionChange = (row) => {
  selectionData.value = row
  emits('selection-change', row)
}
const getSelection = () => {
  return selectionData.value
}

// 单选
const radioValue = ref({})
const handleRadioChange = (value, key) => {
  radioValue.value = value
  emits('radio-change', value)
}

const cellClick = (row, column, cell, event) => {
  emits('cell-click', row, column, cell, event)
}

// 高度计算
const bcTableRef = ref(null)
const resizeObserver = ref(null)
const elTableHright = ref(258)
const resize = () => {
  nextTick(() => {
    // 获取父元素
    const parentDom = bcTableRef.value.parentNode
    // 获取父元素的高度
    const parentHeight = parentDom.offsetHeight
    // 获取元素自身的位置信息相对于窗口顶部距离
    const rectTop = bcTableRef.value.getBoundingClientRect().top
    // 父元素的位置信息相对于窗口顶部距离
    const parentTop = parentDom.getBoundingClientRect().top
    // 元素自身顶部距离父元素顶部的距离
    const elementTop = rectTop - parentTop
    // 分页器高度
    const pageHeight = props.isPage ? 32 + 12 : 0
    elTableHright.value = parentHeight - elementTop - pageHeight - props.pageSpaceHeight - 1
  })
}
const addResizeListener = (fn) => {
  window.onresize = () => {
    fn()
  }
}
const observeBrother = () => {
  resizeObserver.value = new ResizeObserver((entries) => {
    for (let entry of entries) {
      resize()
    }
  })
  // 获取父元素
  const parentDom = bcTableRef.value.parentNode
  const childrens = parentDom.children
  for (var i = 0; i < childrens.length; i++) {
    if (childrens[i] !== bcTableRef.value) {
      resizeObserver.value.observe(childrens[i])
    }
  }
}
onMounted(() => {
  if (props.defaultHeight) {
    elTableHright.value = props.defaultHeight
  } else {
    resize()
    addResizeListener(resize)
    observeBrother()
  }
})
onBeforeUnmount(() => {
  window.onresize = null
  if (resizeObserver.value) {
    resizeObserver.value.disconnect()
    resizeObserver.value = null
  }
})

defineExpose({
  load,
  getSelection,
})
</script>

<style lang="scss" scoped>
.bc-table {
  margin-bottom: 12px;
}
.img-box {
  width: 100%;
  display: flex;
  gap: 5px;
  img {
    width: 32px;
    height: 32px;
  }
}
</style>
